package game;

import java.util.ArrayList;
import java.util.Observer;

import view.ChessGraphicalView;

import command.CastleCommand;

import chess.AIPlayer;
import chess.BishopPiece;
import chess.Board;
import chess.ChessClock;
import chess.GhostPiece;
import chess.KingPiece;
import chess.KnightPiece;
import chess.PawnPiece;
import chess.Piece;
import chess.Player;
import chess.QueenPiece;
import chess.RookPiece;
import chess.Tile;

/**
 * In our game, white will always be at the bottom going up, and black at the top going down
 * @author Max Joyce (Jan 22,2010)
 *
 */
public class ChessGame extends Game {
	public static final String BLACK = "Black";
	public static final String WHITE = "White";
	
	private Player p1;
	private Player p2;
	private ArrayList<Player> players;
	
	public static final int BLACK_ROW = 0;     //represent's black's home (Back) row
	public static final int WHITE_ROW = 7;  //represent white's home (Back) row
	
	public static final int NO_CHECK_STATE   = 0;
	public static final int YOU_ARE_IN_CHECK = 1;
	public static final int YOU_ARE_CHECKING = 2;
	public static final int CHECK_STATE   = 3;
	public static final int CHECKMATE_STATE   = 4;
	public static final int INITIAL_STATE   = 3;
	
	private static final int MOVE_LEFT = -1;
	private static final int MOVE_RIGHT = 1;
	
	public static final int CASTLE_COLUMN_1 = 2;
	public static final int CASTLE_COLUMN_2 = 6; 
	
	private static final int WHITE_TIME_UP=1;
	private static final int BLACK_TIME_UP=2;
	
	public int gameState;
	
	private static final int MAX_POSSIBLE_KILLS_PER_SIDE = 15;
	
	private int moveCount;
	public static final int MOVES_UNTIL_STALEMATE = 50;
	
	private Player currentPlayer;
	private boolean bDone;
	
	private ChessClock clock;
	
	public ChessGame(String blackPlayerName, String whitePlayerName) {
		p1 = new Player(blackPlayerName,this,ChessGame.WHITE);
		p2 = new Player(whitePlayerName,this,ChessGame.BLACK);
		players = new ArrayList<Player>();
		this.getPlayers().add(p1);
		this.getPlayers().add(p2);
		moveCount=0;
		gameState = INITIAL_STATE;
		
		currentPlayer=p1;
		clock = new ChessClock(this);
	}
	
	public ChessGame(chess.Player p1, chess.Player p2){
		this.p1=p1;
		this.p2=p2;
		
		p1.setGame(this);
		p2.setGame(this);
		
		players = new ArrayList<Player>();
		this.getPlayers().add(p1);
		this.getPlayers().add(p2);
		this.gameState = INITIAL_STATE;
		
		currentPlayer=p1;
		clock = new ChessClock(this);
	}

	@Override
	/**
	 * This function will set up the game to be played.  How about
	 * a nice game of chess?
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22,2010)
	 * </pre>
	 */
	public void startup() {
		if (view instanceof ChessGraphicalView){
			((ChessGraphicalView)view).createSideBar(true);
		}
		bDone = false;
		//Base chess game display test
		//Set up Pawns
		for (int i=0;i<Board.BOARD_DIMENSION;i++){
			getBoard().getTile(i, 1).setPieceHere(new PawnPiece(this.p2,this.getBoard().getTile(i,1)));
			getBoard().getTile(i, 6).setPieceHere(new PawnPiece(this.p1,this.getBoard().getTile(i,6)));
		}
		getBoard().getTile(0,0).setPieceHere(new RookPiece(this.p2,this.getBoard().getTile(0,0)));
		getBoard().getTile(1,0).setPieceHere(new KnightPiece(this.p2,this.getBoard().getTile(1,0)));
		getBoard().getTile(2,0).setPieceHere(new BishopPiece(this.p2,this.getBoard().getTile(2,0)));
		getBoard().getTile(3,0).setPieceHere(new QueenPiece(this.p2,this.getBoard().getTile(3,0)));
		getBoard().getTile(4,0).setPieceHere(new KingPiece(this.p2,this.getBoard().getTile(4,0)));
		getBoard().getTile(5,0).setPieceHere(new BishopPiece(this.p2,this.getBoard().getTile(5,0)));
		getBoard().getTile(6,0).setPieceHere(new KnightPiece(this.p2,this.getBoard().getTile(6,0)));
		getBoard().getTile(7,0).setPieceHere(new RookPiece(this.p2,this.getBoard().getTile(7,0)));
		
		getBoard().getTile(0,7).setPieceHere(new RookPiece(this.p1,this.getBoard().getTile(0,7)));
		getBoard().getTile(1,7).setPieceHere(new KnightPiece(this.p1,this.getBoard().getTile(1,7)));
		getBoard().getTile(2,7).setPieceHere(new BishopPiece(this.p1,this.getBoard().getTile(2,7)));
		getBoard().getTile(3,7).setPieceHere(new QueenPiece(this.p1,this.getBoard().getTile(3,7)));
		getBoard().getTile(4,7).setPieceHere(new KingPiece(this.p1,this.getBoard().getTile(4,7)));
		getBoard().getTile(5,7).setPieceHere(new BishopPiece(this.p1,this.getBoard().getTile(5,7)));
		getBoard().getTile(6,7).setPieceHere(new KnightPiece(this.p1,this.getBoard().getTile(6,7)));
		getBoard().getTile(7,7).setPieceHere(new RookPiece(this.p1,this.getBoard().getTile(7,7)));



		
		this.getBoard().drawBoard();

	}

	/**
	 * This function will remove any moves that are illegal only in the game of chess
	 * 
	 * <pre>
	 * Max Joyce
	 * -Created and implemented (Feb 17, 2010)
	 * -Debugged weird issue with aTiles and mTiles assignment.
	 * </pre>
	 * @param curTile The current tile that the piece is on
	 * @param curPlayer The player currently trying to make a move
	 * @param mTiles This is the list of movement Tiles
	 * @param aTiles This is the list of attack tiles
	 */

	@SuppressWarnings("unchecked")
	public void filterBadMoves(Tile curTile, Player curPlayer, ArrayList<Tile> mTiles, ArrayList<Tile> aTiles){
		command.Command testMove=null;
		
		ArrayList<Tile> newMTiles= new ArrayList<Tile>();
		ArrayList<Tile> newATiles= new ArrayList<Tile>();	

		
		Tile tileTested;
		ArrayList<Tile> tilesToCheck;
		
		tilesToCheck=(ArrayList<Tile>) mTiles.clone();  //Make a copy so that mTiles doesn't get changed
		//Find which tiles need to be removed
		for (Tile t:tilesToCheck){
			testMove = new command.MoveCommand(curTile,t);
			//System.out.println("Testing move to "+t.toString());
			tileTested=t;
			testMove.execute();
			
			if ((testForCheck(curPlayer)!= ChessGame.YOU_ARE_IN_CHECK)){ //If it doesn't put you in check
				newMTiles.add(tileTested);  //Keep this move since it doesn't put you in check
				//System.out.println("Keeping "+tileTested.toString());
			}
			testMove.undo();
		}


		
		tilesToCheck.clear();
		tilesToCheck.addAll((ArrayList<Tile>) aTiles.clone());
		//Find which tiles need to be removed
		for (Tile t:tilesToCheck){
			if (t.isPieceHere()){
				testMove = new command.AttackCommand(curTile,t, curPlayer);
			}
			else{
				//En passant attack
				testMove = new command.EnPassantAttackCommand(curTile,t,curPlayer);
				
			}
			tileTested=t;
			testMove.execute();
			if ((testForCheck(curPlayer)!=ChessGame.YOU_ARE_IN_CHECK)){ //If it doesn't put you in check
				newATiles.add(tileTested); //Keep this attack since it doesn't put you in check
				//this.view.printlnToDebugConsole("Keeping "+tileTested.toString());
			}
			testMove.undo();
		}
		

		//Set the valid moves to the movement Tiles
		//NOTE: FOR SOME REASON, Just doing mTiles=newMTiles adds the wrong tiles,
		// so you have to do this.
		mTiles.clear();
		mTiles.addAll(newMTiles);
		
		//Set the valid attacks to the new attack tiles
		//See note above about weirdness with regards to reassigning aTiles.
		aTiles.clear();
		aTiles.addAll(newATiles);

		
	}
	
	private void handleSpecialPawnMoves(Tile curTile,Player curPlayer,ArrayList<Tile> mTiles, ArrayList<Tile>aTiles){
		if (!(curTile.getPieceHere() instanceof PawnPiece)){
			return;
		}
		chess.PawnPiece pawn = (PawnPiece) curTile.getPieceHere();
		Tile attackTile;
		//Remove forward attack
		if (curPlayer.getColour()==ChessGame.WHITE){
			if (curTile.getY()>0){
				attackTile = curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(PawnPiece.MOVE_UP));
				if (attackTile.isPieceHere()){
					if (!attackTile.isYourPieceHere(curPlayer)){
						aTiles.remove(attackTile);
					}
				}
			}

		}
		else if (curPlayer.getColour()==ChessGame.BLACK){
			if (curTile.getY()<Board.BOARD_DIMENSION-1){
				attackTile = curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(PawnPiece.MOVE_DOWN));
				if (attackTile.isPieceHere()){
					if (!attackTile.isYourPieceHere(curPlayer)){
						aTiles.remove(attackTile);
					}
				}
			}
		}
		
		//Add Diagonal Attack
		if (curPlayer.getColour()==ChessGame.WHITE){
			if (curTile.getY()>0){ //If there is even a chance of attacking someone
				//Check for left attack
				if (curTile.getX()>0){
					attackTile = curTile.getBoard().getTile(curTile.getX()-1, curTile.getY()-1);
					if (attackTile.isPieceHere()){//If there is a piece 
						if (!attackTile.isYourPieceHere(curPlayer)){ //And it's not yours
							aTiles.add(attackTile); //Add it to possible attacks
						}
					}
				}
				
				//Check for right attack
				if (curTile.getX()<Board.BOARD_DIMENSION-1){
					attackTile = curTile.getBoard().getTile(curTile.getX()+1, curTile.getY()-1);
					if (attackTile.isPieceHere()){//If there is a piece 
						if (!attackTile.isYourPieceHere(curPlayer)){ //And it's not yours
							aTiles.add(attackTile); //Add it to possible attacks
						}
					}
				}
			}
		}
		else if (curPlayer.getColour()==ChessGame.BLACK){
			if (curTile.getY()<Board.BOARD_DIMENSION-1){ //If there is even a chance of attacking someone
				//Check for left attack
				if (curTile.getX()>0){
					attackTile = curTile.getBoard().getTile(curTile.getX()-1, curTile.getY()+1);
					if (attackTile.isPieceHere()){//If there is a piece 
						if (!attackTile.isYourPieceHere(curPlayer)){ //And it's not yours
							aTiles.add(attackTile); //Add it to possible attacks
						}
					}
				}
				
				//Check for right attack
				if (curTile.getX()<Board.BOARD_DIMENSION-1){
					attackTile = curTile.getBoard().getTile(curTile.getX()+1, curTile.getY()+1);
					if (attackTile.isPieceHere()){//If there is a piece 
						if (!attackTile.isYourPieceHere(curPlayer)){ //And it's not yours
							aTiles.add(attackTile); //Add it to possible attacks
						}
					}
				}
			}
		}//End of add Diagonal Attack
		
		//Check for en passant
		//Add it if it is an option
		
		//Check to see if last move was a pawn
		if (listOfMoves.size()>0){
			command.Command lastCommand=listOfMoves.getLastCommand();
			if (lastCommand instanceof command.MoveCommand){  //It must have been a move to do en passant
				command.MoveCommand lastMove = (command.MoveCommand)lastCommand;
				if (lastMove.getDestTile().getPieceHere() instanceof PawnPiece){ //If it was a pawn moving..
					int moveDistance = (lastMove.getDestTile().getY()-lastMove.getOrigTile().getY());
					if (moveDistance==2 ||moveDistance==-2){ //and it was a double pawn move
						int enPassantPawnX = lastMove.getDestTile().getX();
						int enPassantPawnY = lastMove.getDestTile().getY();
						if ((curTile.getY()==enPassantPawnY)){
							if ((curTile.getX()==enPassantPawnX+1)||(curTile.getX()==enPassantPawnX-1)){
								//handle the en passant move
								if (curPlayer.getColour()==ChessGame.BLACK){
									aTiles.add(curTile.getBoard().getTile(enPassantPawnX, enPassantPawnY+1));
								}else{
									aTiles.add(curTile.getBoard().getTile(enPassantPawnX, enPassantPawnY-1));
								}
								
							}
						}
					}
					
				}
				
			}
		}

		
		
		//Add double move if it is allowed
		if (!pawn.hasMoved()){  //Start of double pawn move
			if (curPlayer.getColour()==ChessGame.WHITE){
				 //Get position above
				if ((!(curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(2*PawnPiece.MOVE_UP)).isPieceHere()))&&(!(curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(PawnPiece.MOVE_UP)).isPieceHere()))){
					mTiles.add(curTile.getBoard().getTile(curTile.getX(), curTile.getY()+(2*PawnPiece.MOVE_UP)));
				}
			}
		    else if (curPlayer.getColour()==ChessGame.BLACK){
				 //Get position below
		    	if ((!(curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(2*PawnPiece.MOVE_DOWN)).isPieceHere()))&&(!(curTile.getBoard().getTile(curTile.getX(),  curTile.getY()+(PawnPiece.MOVE_DOWN)).isPieceHere()))){
		    		mTiles.add(curTile.getBoard().getTile(curTile.getX(), curTile.getY()+(2*PawnPiece.MOVE_DOWN)));	
		    	}
			 }
		}//End of Double Pawn Move
	}
	
	
	/**
	 * This function will add special moves to pieces if they are allowed, eg:
	 * Double pawn move - if The pawn hasn't moved  (DONE)
	 * En Passant - If a pawn double moved, then another pawn cuts behind it
	 * Pawn attacks diagonally, not forward   (DONE)
	 * Castling - If rook and king haven't moved, the king can move 2 towards the rook, rook
	 *            goes to the other side.  Spots in between must be empty,
	 *            and can't be being threatened by opponent pieces (Initiated by king)\
	 * 
	 * @param curTile  The tile with the piece being used
	 * @param curPlayer The current Player
	 * @param mTiles List of current movement tiles
	 * @param aTiles List of current attack tiles
	 */
	public void addSpecialChessMoves(Tile curTile, Player curPlayer, ArrayList<Tile> mTiles, ArrayList<Tile> aTiles){
		chess.Piece piece = curTile.getPieceHere();
		if (piece instanceof KingPiece){
			chess.KingPiece king = (chess.KingPiece) piece;
			//Add castling if allowed
			if (!king.hasMoved() && !isTileThreatened(curTile,curPlayer)){  //The king must not have moved and not be in check
				//Find the rooks
				ArrayList<RookPiece> rooks = new ArrayList<RookPiece>(2); //Create an arraylist with room for two
				
				for (Piece p:curPlayer.getMyPieces()){ //Get the rooks
					if (p instanceof RookPiece){
						if (!p.isDead()){
							rooks.add((RookPiece) p); //Keep track of the rooks
						}
					}
				}
				
				for (RookPiece rook:rooks){
					if (!rook.hasMoved()){ //If the rook has also not moved
						//Determine if the king can castle with this rook
						int x=rook.getCurrentTile().getX();
						int y=rook.getCurrentTile().getY();
						int change=0;
						
						if (x<king.getCurrentTile().getX()){
							change=ChessGame.MOVE_RIGHT;  //Need to go right to get to the king
						}
						else{
							change=ChessGame.MOVE_LEFT; //Need to go left to get to the king
						}
						Tile t=curTile.getBoard().getTile(x+change, y);
						x=x+change;
						
						//The tiles between the rook and king must be empty and not threatened
						while(!t.isPieceHere()&&!isEmptyTileThreatened(t,curPlayer)){
							t=curTile.getBoard().getTile(x, y);  
							x=x+change;                          
						}
						if (t==curTile){
							//You can theoretically castle
							mTiles.add(curTile.getBoard().getTile(curTile.getX()-(2*change), curTile.getY()));
						}
						
					}
				}
			}
		}
		else if (piece instanceof PawnPiece){
			handleSpecialPawnMoves(curTile,curPlayer,mTiles,aTiles);
		}
	
	}
	
	/**
	 * This lets a player take a turn
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22, 2010)
	 * -Pushed it closer to an actual chess game turn (Jan 24, 2010)
	 * -You can now only move your own pieces (Jan 26, 2010)
	 * -Added the ability to deselect a piece by entering its current coords as
	 *  the destination (Feb 10,2010)
	 * 
	 * </pre>
	 */	
	public void takeTurn(Player p){
		
		//Going to set a few things to null here to satisfy Eclipse;
		ArrayList<Tile> attackTiles;
		ArrayList<Tile> movementTiles=null;
		boolean repickPiece=true; //You want to go through the loop at least once
		Tile dest=null,t=null;
		command.Command newMove=null;
		Piece piece=null;
		
		if (testForCheck(p)==ChessGame.YOU_ARE_IN_CHECK){
			this.view.showOutputMessage(p.getColour()+" is in Check");
		}
			
		
		
		while (repickPiece){
			view.displayBoard();
			repickPiece = false; //This should be false by default, so it only happens when you want it to.
			//Pick a piece to move 
			this.view.printlnToDebugConsole("Pick a Piece");
			t=chooseTile();
			if(t == null){
				return;
			}
			p = currentPlayer;
			if (!t.isYourPieceHere(p)){ //If there isn't one of your pieces in the selected tile
				while (!t.isYourPieceHere(p)){
					p = currentPlayer;
					t=chooseTile(); //Keep trying to get one of your pieces
					p = currentPlayer;
					if(t == null){
						return;
					}
				}
			}
		
			//Generate moves for piece
			piece=t.getPieceHere();
			movementTiles=piece.getMovementTiles();
			
			attackTiles=findAttackMoves(movementTiles,p);
			addSpecialChessMoves(t,p,movementTiles,attackTiles); //Should go here, after attackTiles, and before bad moves are filtered
			filterBadMoves(t,p,movementTiles,attackTiles); //Filter out things that would put you in
														   //check!
		
		
			
			view.showPossibleMoves(movementTiles, attackTiles, t);
			
			//Find a new place for the piece 
			if (!(attackTiles.contains(dest) || movementTiles.contains(dest))){ //If the selected tile isn't a valid location 
				while ((!(attackTiles.contains(dest) || (movementTiles.contains(dest))))&&(!repickPiece)){
					this.view.printlnToDebugConsole("Pick a destination");
					dest=chooseTile(); //Keep trying to get a valid tile
					if(dest == null){
						return;
					}
					this.view.printlnToDebugConsole("You picked " + dest.toString());
					
					//If it is the same as the current tile
					if (dest.equals(t)){
						repickPiece=true; //You want to repick the piece
					}
				}
			}
			//System.out.print("Picked");
			
		} //End of repickPiece loop


		//Actually move the piece if it was a move
		if (movementTiles.contains(dest)){
			newMove = new command.MoveCommand(t,dest);	
		}
		else{ //Do the attack if it was an attack
			if (dest.isPieceHere()){
				newMove = new command.AttackCommand(t,dest,p);
			}
			else{
				// En passant attack
				newMove = new command.EnPassantAttackCommand(t,dest,p);
			}
			
		}

		newMove.execute();  //Execute the command
		
		listOfMoves.add(newMove); //Add this command to the list of moves made
		
		//Check to see if the move was a castle
		if (piece instanceof KingPiece){
				if ((dest.getX()==ChessGame.CASTLE_COLUMN_1)||(dest.getX()==ChessGame.CASTLE_COLUMN_2)){
					if(!newMove.hadMovedPreviously()){
						finishCastleMove(p,dest);//Castle
					}
				}
		}
		
		//Check if it is a pawn in the last row, if so, queen it
		if (piece instanceof PawnPiece){
			if (dest.getY() == ChessGame.BLACK_ROW || dest.getY()== ChessGame.WHITE_ROW){
				queenMe(dest,p);
			}
		}

	}

	/**
	 * This function does the other half of the castle move - that is, moves the rook
	 * to the other side of your king, using the secondary command, CastleCommand.
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 20,2010)
	 * </pre>
	 * 
	 * @param curPlayer  The player who did the castle move
	 * @param destTile   The tile that the king moved to.
	 */
	public void finishCastleMove(Player curPlayer, Tile destTile){
		//Find rook
		RookPiece rook=null;
		Tile rookDest;
		command.CastleCommand rookMove;
		if (destTile.getX()==ChessGame.CASTLE_COLUMN_1){
			//Get Left Rook
			for (Piece p: curPlayer.getMyPieces()){
				if (p instanceof RookPiece){
					if (p.getCurrentTile().getX()==0){
						rook=(RookPiece)p;  //This is the correct one one
					}
				}
			}	
		}
		else{
			//Get right rook
			for (Piece p: curPlayer.getMyPieces()){
				if (p instanceof RookPiece){
					if (p.getCurrentTile().getX()==7){
						rook=(RookPiece)p;  //This is the right one
					}
				}
			}
		}
		
		//Find the tile that it must move to
		if (destTile.getX()==ChessGame.CASTLE_COLUMN_1){
			//Must move to x==3
			rookDest=destTile.getBoard().getTile(ChessGame.CASTLE_COLUMN_1+1, destTile.getY());
		}
		else{
			//Must move to x==5
			rookDest=destTile.getBoard().getTile(ChessGame.CASTLE_COLUMN_2-1, destTile.getY());
		}
		
		//Create Castle Command
		rookMove=new CastleCommand(rook.getCurrentTile(),rookDest);
		
		//Execute it
		rookMove.execute();
		
		//Add it to the last command in the list.
		listOfMoves.getLastCommand().addSecondaryCommand(rookMove);
	}
	
	
	/**
	 * This will allow you to play your game of chess
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22,2010)
	 * </pre>
	 */	
	public void play(){
		//Loop
		boolean bStale=false;
		int timeUp=0;  
		currentPlayer=this.getPlayers().get(0);
		while(!bDone){
			this.view.updateCurrentPlayerTurn(currentPlayer);
			this.view.printlnToDebugConsole("Check: "+this.testForCheck(currentPlayer));
			clock.startNewTurn(currentPlayer);  //Start the new turn with the new player for the clock
			currentPlayer.takeTurn();

			if(listOfMoves.getCurrentSize() + 1 != 0){
				//Handle the 50 moves rule
				command.Command lastCommand = listOfMoves.getLastCommand();
				command.ChessMoveCountCommand moveCountCommand=null;
				if (lastCommand instanceof command.AttackCommand){
					moveCountCommand = new command.ChessMoveCountCommand(moveCount,0,this);
					
				}
				else if (lastCommand instanceof command.MoveCommand){
					if (((command.MoveCommand) lastCommand).getDestTile().getPieceHere() instanceof PawnPiece){
						moveCountCommand = new command.ChessMoveCountCommand(moveCount,0,this);
					}
					else{
						moveCountCommand = new command.ChessMoveCountCommand(moveCount,moveCount+1,this);
					}
				}
				else{
					moveCountCommand = new command.ChessMoveCountCommand(moveCount,moveCount+1,this);
				}
				moveCountCommand.execute();
				lastCommand.addSecondaryCommand(moveCountCommand);
				
				//Now add the clock command
				command.ChessClockCommand clockCommand=new command.ChessClockCommand(this, clock.getElapsedTime(),this.currentPlayer);
				clockCommand.execute();
				lastCommand.addSecondaryCommand(clockCommand);
				
			}
			
			switchPlayer();

			
			//System.out.println("Black: "+clock.getBlackTime());
			//System.out.println("White: "+clock.getWhiteTime());
			//Test for end conditions before the player gets a chance to take their turn
			if (testForCheckMate(currentPlayer)){
				gameState = CHECKMATE_STATE;
				bDone=true;  //If it is a check or stale mate, the game is over
			}else if(testForStaleMate(currentPlayer) || moveCount==ChessGame.MOVES_UNTIL_STALEMATE){
				gameState = NO_CHECK_STATE;
				bDone=true;
				bStale=true;
			}else if (clock.getBlackTime()>=ChessClock.MAXTIME){
				bDone=true;
				timeUp=BLACK_TIME_UP;
				//System.out.println("Black time up");
			}else if (clock.getWhiteTime()>=ChessClock.MAXTIME){
				bDone=true;
				timeUp=WHITE_TIME_UP;
				//System.out.println("white time up");
			}
		}//while (!bDone)
		getBoard().drawBoard();
		if (moveCount==ChessGame.MOVES_UNTIL_STALEMATE){
			this.view.showOutputMessage("Stalemate: 50 Moves passed with no attacks or pawn moves");
		}else if (timeUp==BLACK_TIME_UP){
			this.view.showOutputMessage("Black is out of time!");
		}else if (timeUp==WHITE_TIME_UP){
			this.view.showOutputMessage("White is out of time!");
		}
		else if (bStale && !isNewGame){
			this.view.showOutputMessage("Stalemate");
		}else if (!isNewGame){
			switchPlayer();
			this.view.showOutputMessage("Checkmate: "+currentPlayer.getName()+" wins!");
		}

	}
	
	/**
	 * This is used for the 50 moves rule, and for undoing moves
	 */
	public void setMoveCounter(int newCount){
		moveCount=newCount;
	}
	
	/**
	 * This method will switch the current player
	 */
	private void switchPlayer(){
		if(currentPlayer != null){
			if (currentPlayer==this.getPlayers().get(0)){
				currentPlayer=this.getPlayers().get(1);
			}
			else{
				currentPlayer=this.getPlayers().get(0);
			}
		}
		
	}
	/**
	 * This method allows the promotion of a pawn into one of the promotable choices
	 * @return When pawn reaches the opponent's home row he can rank up
	 * <pre>
	 * Samual Martelli
	 * -Made method signature (Jan 25,2010)
	 * Max Joyce
	 * -Fully implemented, including choice of piece wanted (Jan 27, 2010)
	 * </pre>
	 */
	public void queenMe(Tile t, Player p) {
		command.Command lastCommand = listOfMoves.getLastCommand(); //Get the last command in the list
		command.PromoteCommand promotion;
		String pieceWanted="";
		Object[] choices={"Queen","Knight","Bishop","Rook"};
		
		if (t.getY() == WHITE_ROW && p.getColour()==ChessGame.BLACK){ //If you are on the opposite side
			if (p instanceof chess.BadAIChessPlayer || p instanceof chess.BetterAIChessPlayer){  //If it is a computer
				pieceWanted="Queen";  //Just give it a queen
			}
			else{ //But if it is a normal player, 
				pieceWanted=view.getUserChoice(choices, "What piece do you want?");  //Ask it what it wants
			}
			
			
			promotion = new command.PromoteCommand(pieceWanted,t); //Make the command
			promotion.execute(); //Do it
			lastCommand.addSecondaryCommand(promotion); //Add it as the secondary command of the
														//last move

			
		}
		else if (t.getY() == BLACK_ROW && p.getColour()==ChessGame.WHITE){ //On the opposite side
			
			if (p instanceof chess.BadAIChessPlayer|| p instanceof chess.BetterAIChessPlayer){  //If it is a computer
				pieceWanted="Queen";  //Just give it a queen
			}
			else{ //But if it is a normal player, 
				pieceWanted=view.getUserChoice(choices, "What piece do you want?");  //Ask it what it wants
			}
			
			promotion = new command.PromoteCommand(pieceWanted,t); //Make the promotion command
			promotion.execute();  //Do it
			lastCommand.addSecondaryCommand(promotion); //Add it as the secondary command of the
														//last move
		}
		
	}
	
	/**
	 * This method checks to see if it is safe to move to a tile.
	 * It is assumed that the tile will be empty that is being checked.
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 21,2010)
	 * </pre>
	 */
	public boolean isEmptyTileThreatened(Tile t, Player currentPlayer){
		GhostPiece placeHolderPiece=new GhostPiece(currentPlayer,t);
		t.setPieceHere(placeHolderPiece);
		boolean threatened = isTileThreatened(t,currentPlayer);
		t.setPieceHere(null);
		currentPlayer.getMyPieces().remove(placeHolderPiece);
		return threatened;
	}
	
	/**
	 * This method checks to see if it is safe to move to a tile.
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 20,2010)
	 * -Changed to be for an occupied tile(Feb 21,2010)
	 * </pre>
	 */
	public boolean isTileThreatened(Tile t, Player currentPlayer){
		Player opponent;
		ArrayList<Tile> movementTiles;
		ArrayList<Tile> attackTiles;
		
		boolean threatened=false;

		
		//Get the other player
		if (currentPlayer==p1){
			opponent=p2;
		}
		else{
			opponent=p1;
		}

		
		//Check to see if it is safe
		for (Piece p:opponent.getMyPieces()){
			if (!p.isDead()){
				movementTiles=p.getMovementTiles();
				attackTiles=findAttackMoves(movementTiles,opponent);
				
				//The only piece with special moves that would affect check are pawns
				if (p instanceof PawnPiece){
					handleSpecialPawnMoves(p.getCurrentTile(), opponent, movementTiles, attackTiles);
				}
				
				if (attackTiles.contains(t)){
					threatened=true;
				}
			}
		}
		
		
		return threatened;
		
	}
	
	/**
	 * This will check to see if you are currently in check,
	 * or if you are putting the opponent in check.
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 10,2010)
	 * -Simplified the method greatly by using the isTileThreatened
	 *  method.  (Feb 21,2010)
	 * </pre>
	 * @param currentPlayer This is the current player
	 * @return An int describing the check state.
	 */
	public int testForCheck(Player currentPlayer){
		Player opponent;
		Tile yourKingTile=null,opponentKingTile=null;

		
		//Get the other player
		if (currentPlayer==p1){
			opponent=p2;
		}
		else{
			opponent=p1;
		}
		
		//Find your king
		for (Piece p:currentPlayer.getMyPieces()){
			if (p instanceof KingPiece){
				yourKingTile=p.getCurrentTile();
			}
		}
		
		//Find your opponent's king
		for (Piece p:opponent.getMyPieces()){
			if (p instanceof KingPiece){
				opponentKingTile=p.getCurrentTile();
			}
		}

		if (isTileThreatened(yourKingTile,currentPlayer)){
			gameState = CHECK_STATE;
			return YOU_ARE_IN_CHECK;
		}
		
		
		if (isTileThreatened(opponentKingTile,opponent)){
			gameState = CHECK_STATE;
			return YOU_ARE_CHECKING;
		}
		return NO_CHECK_STATE;
	}
	
	/**
	 * This function tests to see if a piece has no moves
	 * @param p The piece to be tested
	 * @return If the piece has no moves, then true, otherwise false 
	 */
	private boolean hasNoMoves(Piece p, Player owningPlayer){
		if (!p.isDead()){
			ArrayList<Tile> movementTiles=p.getMovementTiles();
			ArrayList<Tile> attackTiles=findAttackMoves(movementTiles,owningPlayer);
			addSpecialChessMoves(p.getCurrentTile(),owningPlayer,movementTiles,attackTiles);
			filterBadMoves(p.getCurrentTile(),owningPlayer,movementTiles,attackTiles); //Filter out things that would put you in
														   //check!
			return movementTiles.isEmpty() && attackTiles.isEmpty();
		}
		return true; //The piece is dead, so of course it has no moves!
	}
	
	/**
	 * This function tests to see if you are in checkmate, and have lost
	 * <pre>
	 * Max Joyce
	 * -Created and implemented (Feb 17, 2010)
	 * </pre>
	 * @param currentPlayer The current player, and the one who is being tested to see if he lost.
	 * @return True if you are in checkmate, false if not
	 */
	public boolean testForCheckMate(Player currentPlayer){
		boolean mated = true;
		if (testForCheck(currentPlayer)==ChessGame.YOU_ARE_IN_CHECK){
			for (Piece p: currentPlayer.getMyPieces()){
				mated = (mated && hasNoMoves(p, currentPlayer));  //Only become false if a piece has a move
			}
			return mated;
		}
		return false; //If you aren't even in check, you can't be mated
	}
	
	/**
	 * This function tests to see if you are in a stalemate, and the game needs to be a draw
	 * <pre>
	 * Max Joyce
	 * -Created and implemented (Feb 17, 2010)
	 * </pre>
	 * 
	 * @param currentPlayer The player who is being tested for a stalemate
	 * @return True if in stalemate, false if not
	 */
	public boolean testForStaleMate(Player currentPlayer){
		boolean stalemate = true;
		
		if ((players.get(0).getKills().size()==MAX_POSSIBLE_KILLS_PER_SIDE)&&(players.get(1).getKills().size()==MAX_POSSIBLE_KILLS_PER_SIDE)){  //If both players have killed all but the king
			return true;
		}
		
		if (testForCheck(currentPlayer)==ChessGame.NO_CHECK_STATE){
			for (Piece p: currentPlayer.getMyPieces()){
				stalemate = (stalemate && hasNoMoves(p, currentPlayer));  //Only become false if a piece has a move
			}
			return stalemate;
		}
		return false;  //If you aren't in a no check state, it can't be a stalemate
	}
	
	/**
	 * This function will return to a piece the attacks that it can make
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22,2010)
	 * -Changed to returning an ArrayList rather than a generic List (Jan 24,2010)
	 * </pre>
	 */	
	
	public ArrayList<Tile> findAttackMoves(ArrayList<Tile> movementTiles, Player p){
		ArrayList<Tile> attackTiles = new ArrayList<Tile>();
		ArrayList<Tile> removeTiles = new ArrayList<Tile>();
		
		for (Tile t: movementTiles){
			if (t.isPieceHere()){
				//prepare to remove the tile
				removeTiles.add(t);
				
				//if it is an opponents piece
				if (!(t.isYourPieceHere(p))){
					attackTiles.add(t); //Add the tile to the list of attack tiles
				}
				
			}
		}
		
		//Remove the tiles that need to be removed.
		for (Tile t:removeTiles){
			movementTiles.remove(t);
		}
		
		return attackTiles;
	}
	
	public ArrayList<Player> getPlayers(){
		return players;
	}
	
	
	/**
	 * This function will return the current state of the game
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22,2010)
	 * -Changed to returning an ArrayList rather than a generic List (Jan 24,2010)
	 * </pre>
	 */	
	public int getGameState(){
		return gameState;
	}
	/**
	 * This method will undo moves in a current game
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void undo(){
		if(listOfMoves.undo()){
			switchPlayer();
		}
		if(players.get(1) instanceof AIPlayer){
			if(listOfMoves.undo()){
				switchPlayer();
			}
		}
		view.displayBoard();
		this.view.updateCurrentPlayerTurn(currentPlayer);
	}
	/**
	 * This method will redo current moves in a game
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void redo(){
		if(listOfMoves.redo()){
			switchPlayer();
		}
		if(players.get(1) instanceof AIPlayer){
			if(listOfMoves.redo()){
				switchPlayer();
			}
		}
		view.displayBoard();
		this.view.updateCurrentPlayerTurn(currentPlayer);
	}
	/**
	 * This gets a string interpretation of the move list
	 * @return Returns a string of the move list
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public String getMoveList(){
		return listOfMoves.toString();
	}
	/**
	 * This method will tell the current game that the new game option has been selected
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void newGameSelected(){
		bDone = true;
		isNewGame = true;
	}
	/**
	 * This method will set the current player based on the player index
	 * @param player The index of the current player
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void setPlayer(int player){
		if(player < players.size()){
			currentPlayer = players.get(player);
		}
	}
	/**
	 * This method will get the current player 
	 * @return Returns a player object which is the current player
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public Player getCurrentPlayer(){
		return currentPlayer;
	}
	/**
	 * This method will get the opponent of the current player
	 * @param curPlayer The player currently playing the game
	 * @return Returns the opponent from the current player
	 */
	public chess.Player getOpponent (chess.Player curPlayer){
		if (curPlayer==p1){
			return p2;
		}
		else{
			return p1;
		}
	}
	public void setMoveList(MoveExecutionList list){
		super.setMoveList(list);
		command.Command c = list.getLastCommand().getSecondaryCommand();
		if(c == null){
			return;
		}
		if(c instanceof command.ChessMoveCountCommand){
			command.ChessMoveCountCommand count = (command.ChessMoveCountCommand)c;
			moveCount = count.getEndCount();
			return;
		}
		while(c.getSecondaryCommand() == null || !(c instanceof command.ChessMoveCountCommand)){
			c = c.getSecondaryCommand();
		}
		if(c == null){
			return;
		}
		command.ChessMoveCountCommand count = (command.ChessMoveCountCommand)c;
		moveCount = count.getEndCount();
	}

	public ChessClock getClock() {
		// TODO Auto-generated method stub
		return clock;
	}

}
